#include "Nodes.h"

int main(int argc, char** argv){

int ret = 0;
int nodes, rank;
int idOffset = 0;
int bodys = 0;
int simulationSteps = 0;

int* workload = NULL;
int work = 0;
	
timeVal start,end;
unsigned long runtime;
double dt = 2000.0;
	
MPI_Init(&argc, &argv);

if(argc != 3 && argc != 4){
	usage();
	ret = -1;
}

if(ret == 0){

	Body* nBodys = NULL;

	MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	MPI_Comm_size(MPI_COMM_WORLD, &nodes);

	workload = new int[nodes];

	/* INIT */
	if(rank == 0 ){
		bodys = atoi(argv[1]);
		distributeWork(nodes,bodys,workload);
#if DEBUG == 1
		std::cout << "# General Runtime-Informations #################" << std::endl
			  << "# Sizeof Datatypes Particles/Vector/Body:" << std::endl
		  	  << "# Sizeof Particle :" << sizeof(Particle) << " Byte" <<  std::endl
		  	  << "# Sizeof Vector   :" << sizeof(Vector) << " Byte" <<  std::endl
		 	  << "# Sizeof Body     :" << sizeof(Body) << " Byte" <<  std::endl
			  << "################################################" << std::endl;
#endif
	}

	simulationSteps = atoi(argv[2]);

	if(argc == 4){
		dt = atof(argv[3]);
	}
	

	if(nodes > 1){
		//distribute workload
		MPI_Bcast(workload, nodes, MPI_INT, 0, MPI_COMM_WORLD);

		work = workload[rank];
		std::cout << "# Node " << rank << " workload " << work << std::endl;
		
		if(work != 0){
			idOffset = 0;
			
			nBodys = new Body[work];
			
			if(rank > 0 && workload[rank -1 ] != work){
				idOffset = workload[rank - 1] * rank;
			}else{
				idOffset = work*rank;
			}
			
			//std::cout << "# Node " << rank << "idOffset " << idOffset << std::endl;

			for(int i = 0; i < work; i++){	
				nBodys[i].init(idOffset + i, dt);
			}

			/* synchronize Nodes */
			MPI_Barrier(MPI_COMM_WORLD);

			/* do work */
			gettimeofday(&start,NULL);
			runtime = doWork(rank,nodes,simulationSteps,work,bodys,nBodys);
			gettimeofday(&end,NULL);	
		}
	}else{
		std::cout <<"# Single-Node execution: "<< std::endl;
		nBodys = new Body[bodys];
		
		for(int i = 0; i < bodys; i++){
			nBodys[i].init(i,dt);
		}
		
		/* do whole work on a single node*/
		gettimeofday(&start,NULL);
		runtime = doWorkOnSingleNode(simulationSteps,bodys,nBodys);
		gettimeofday(&end,NULL);
	}
	
	if(rank == 0){
		//print results
		std::cout << "# Runtime_Informations" << std::endl
		<< " [Nodes] " << nodes 
		<< " [Bodys] " << bodys 
		<< " [Steps] " << simulationSteps 
		<< " [CALC_Runtime(usec.)] " << runtime 
		<< " [CALL_Runtime(usec.)] " << calcRuntime(start,end)
		<< std::endl;  
	}else{//send results to node 0(?)
	}
	
	if(workload != NULL){
		delete [] workload;
		workload = NULL;
	}

	if(nBodys != NULL){
		delete [] nBodys;
		nBodys = NULL;	
	}
}
	
	MPI_Finalize();

	return ret;
}
